/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frmem.c                                            */
/*  NOTE      =                                                    */
/*  DATE      = 2014/10/10 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#include "frcomm.h"
#include "frmem_ncx.h"
#include "ncx_slab.h"

#if FR_MEM_POOL

    typedef struct
    {
        char arr_name[30];
        unsigned char  *p_space;
        ncx_slab_pool_t *p_st_sp;
    } Frmempool;

    #define FR_MEMPOOL_COUNT_MAX    500
    typedef struct
    {
        int i_count; //当前使用的个数
        int i_max;   //最大多少个
        Frmempool *p_mempool[FR_MEMPOOL_COUNT_MAX];
    } Frmempool_mng;

    /* 该链表能看到所有模块(so)的共享缓存池 */
    Frmempool_mng g_st_mempool_mng={0,FR_MEMPOOL_COUNT_MAX};
    static pthread_rwlock_t  rwlock_mempool = PTHREAD_RWLOCK_INITIALIZER;

    /* 使用 static后，每个动态链接库看到不通的p_g_st_mem_pool
     * 由此实现不通模块使用不同的共享缓存池 */
    static Frmempool *p_g_st_mempool = NULL;

    /*******************************************************************************
    *  FUNC     :  释放Frmem_pool
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    static void __fr_free_mempool(Frmempool **pp_st_mempool)
    {
        Frmempool *p_st_mempool;

        if(pp_st_mempool == NULL || *pp_st_mempool == NULL)
            return;

        p_st_mempool = *pp_st_mempool;
        if(p_st_mempool->p_space)
        {
            pthread_spin_destroy(&p_st_mempool->p_st_sp->mutex.spin);
            free(p_st_mempool->p_space);
            p_st_mempool->p_space = NULL;

            p_st_mempool->p_st_sp = NULL;
        }

        free(p_st_mempool);
        *pp_st_mempool = NULL;

        return;
    }

    /*******************************************************************************
    *  FUNC     :  按顺序插入(从小到大排序)
    *  ARGS     :  p_st_mempool     (Frmempool *)
    *  RTN      :  FR_SUCC (success)
    *           :  FR_FAIL (failed)
    *  NOTE     :
    *******************************************************************************/
    static int __fr_mempools_insert(Frmempool *p_st_mempool)
    {
        int ui_idx;
        unsigned char *p_space;
        void *p_dst, *p_src;
        Frmempool *p_st_mempool_pri=NULL, *p_st_mempool_next=NULL;

        if(p_st_mempool == NULL)
            return FR_FAIL;

        p_space = p_st_mempool->p_space;
        if(p_space == NULL)
            return FR_FAIL;

        //没有节点
        if(g_st_mempool_mng.i_count <= 0)
        {
            g_st_mempool_mng.p_mempool[g_st_mempool_mng.i_count] = p_st_mempool;
            g_st_mempool_mng.i_count=1;
            return FR_SUCC;
        }

        //只有1个节点
        if(g_st_mempool_mng.i_count == 1)
        {
            p_st_mempool_pri = g_st_mempool_mng.p_mempool[0];
            if ( (unsigned long long)p_space > (unsigned long long)(p_st_mempool_pri->p_st_sp->end) )
            {
                g_st_mempool_mng.i_count++;
                g_st_mempool_mng.p_mempool[1] = p_st_mempool;
                return FR_SUCC;
            }

            g_st_mempool_mng.i_count++;
            g_st_mempool_mng.p_mempool[0] = p_st_mempool;
            g_st_mempool_mng.p_mempool[1] = p_st_mempool_pri;
            return FR_SUCC;
        }

        //插入到首节点
        if ( (unsigned long long)p_space < (unsigned long long) (g_st_mempool_mng.p_mempool[0]->p_st_sp->start) )
        {
            p_src = (void *)g_st_mempool_mng.p_mempool;
            p_dst = (void *)g_st_mempool_mng.p_mempool + sizeof(Frmempool *);
            memmove(p_dst, p_src, sizeof(Frmempool *) * g_st_mempool_mng.i_count);
            g_st_mempool_mng.p_mempool[0] = p_st_mempool;
            g_st_mempool_mng.i_count++;
            return FR_SUCC;
        }

        //插入到中间
        for(ui_idx=0; ui_idx < g_st_mempool_mng.i_count-1; ui_idx++)
        {
            p_st_mempool_pri = g_st_mempool_mng.p_mempool[ui_idx];
            p_st_mempool_next = g_st_mempool_mng.p_mempool[ui_idx+1];
            if(p_st_mempool_pri == NULL || p_st_mempool_next == NULL)
                break;

            if ( (unsigned long long)p_space > (unsigned long long)(p_st_mempool_pri->p_st_sp->end)
                    && (unsigned long long)p_space < (unsigned long long)(p_st_mempool_next->p_st_sp->start) )
            {
                p_src = (void *)g_st_mempool_mng.p_mempool + sizeof(Frmempool *) * (ui_idx +1);
                p_dst = p_src + sizeof(Frmempool *);
                memmove(p_dst, p_src, sizeof(Frmempool *) * (g_st_mempool_mng.i_count -ui_idx-1) );
                g_st_mempool_mng.p_mempool[ui_idx+1] = p_st_mempool;
                g_st_mempool_mng.i_count++;
                return FR_SUCC;
            }
        }

        //放在最后
        g_st_mempool_mng.p_mempool[g_st_mempool_mng.i_count] = p_st_mempool;
        g_st_mempool_mng.i_count++;
        return FR_SUCC;
    }

    /*******************************************************************************
    *  FUNC     :  按顺序插入(从小到大排序)
    *  ARGS     :  ui_idx       (删除指定节点)
    *  RTN      :  FR_SUCC (success)
    *           :  FR_FAIL (failed)
    *  NOTE     :
    *******************************************************************************/
    static int __fr_mempools_del(unsigned int ui_idx)
    {
        void *p_dst, *p_src;
        if(ui_idx+1 > g_st_mempool_mng.i_count )
            return FR_FAIL;

        if(g_st_mempool_mng.i_count <= 0)
            return FR_FAIL;

        if(ui_idx == 0)
        {
            p_dst = (void *)g_st_mempool_mng.p_mempool;
            p_src = p_dst + sizeof(Frmempool *);
            memmove(p_dst, p_src, (g_st_mempool_mng.i_count-1) * sizeof(Frmempool *));
            g_st_mempool_mng.i_count--;
            return FR_SUCC;
        }

        if(ui_idx+1 == g_st_mempool_mng.i_count)
        {
            g_st_mempool_mng.i_count--;
            return FR_SUCC;
        }

        p_dst = (void *)g_st_mempool_mng.p_mempool + sizeof(Frmempool *) * ui_idx;
        p_src = p_dst + sizeof(Frmempool *);
        memmove(p_dst, p_src, (g_st_mempool_mng.i_count- ui_idx -1) * sizeof(Frmempool *));
        g_st_mempool_mng.i_count--;

        return FR_SUCC;
    }

    /*******************************************************************************
    *  FUNC     :  缓存池句柄(Frmempool*)查询
    *  ARGS     :  p_free_data      (内存地址)
    *  RTN      :  Frmempool*       (没有时，返回NULL)
    *  NOTE     :
    *******************************************************************************/
    static Frmempool *__fr_mempools_find(unsigned char *p_free_data)
    {
        int i_high, i_low, i_midle;
        Frmempool *p_st_mempool_find=NULL;

        if(g_st_mempool_mng.i_count <= 0)
            return NULL;

        if(g_st_mempool_mng.i_count == 0)
        {
            p_st_mempool_find = g_st_mempool_mng.p_mempool[0];
            if(p_free_data >= p_st_mempool_find->p_st_sp->start && p_free_data <= p_st_mempool_find->p_st_sp->end)
                return p_st_mempool_find;
            else
                return NULL;
        }

        i_high = g_st_mempool_mng.i_count -1;
        i_low = 0;
        i_midle = g_st_mempool_mng.i_count/2;

        while(i_high >= i_low)
        {
            i_midle = (i_high + i_low)/2;
            p_st_mempool_find = g_st_mempool_mng.p_mempool[i_midle];

            if(p_free_data >= p_st_mempool_find->p_st_sp->start && p_free_data <= p_st_mempool_find->p_st_sp->end)
               return p_st_mempool_find;

            if(p_st_mempool_find->p_st_sp->start > p_free_data)
               i_high = i_midle - 1;         //从小到大排序
            else
               i_low = i_midle + 1;
        }

        return NULL;
    }

    /*******************************************************************************
    *  FUNC     :  缓存池初始化
    *  ARGS     :
    *  RTN      :  FR_SUCC (success)
    *           :  FR_FAIL (failed)
    *  NOTE     :
    *******************************************************************************/
    int  __fr_slab_init(size_t s_size, const char *p_slab_name)
    {
        u_char *p_space = NULL;
        int i_ret_fun;

        if(p_g_st_mempool)
            return FR_SUCC;

        if(s_size < 4194304)//4M
            s_size = 4194304;

        p_space = malloc(s_size);
        if(p_space == NULL)
            return FR_FAIL;

        p_g_st_mempool = malloc(sizeof(Frmempool));
        if(p_g_st_mempool == NULL)
        {
            free(p_space);
            return FR_FAIL;
        }
        memset(p_g_st_mempool, 0x00, sizeof(Frmempool));
        if(p_slab_name)
            snprintf(p_g_st_mempool->arr_name, sizeof(p_g_st_mempool->arr_name), "%s", p_slab_name);
        else
            p_g_st_mempool->arr_name[0] = '\0';

        p_g_st_mempool->p_space = p_space;
        p_g_st_mempool->p_st_sp = (ncx_slab_pool_t*) p_space;
        p_g_st_mempool->p_st_sp->addr = p_space;
        p_g_st_mempool->p_st_sp->min_shift = 3;
        p_g_st_mempool->p_st_sp->end = p_space + s_size;
        ncx_slab_init(p_g_st_mempool->p_st_sp);
        pthread_spin_init(&p_g_st_mempool->p_st_sp->mutex.spin, 0);

        pthread_rwlock_wrlock(&rwlock_mempool);
        if(g_st_mempool_mng.i_count >= g_st_mempool_mng.i_max)
        {
            pthread_rwlock_unlock(&rwlock_mempool);
            __fr_free_mempool(&p_g_st_mempool);
            return FR_FAIL;
        }

        //从小到大插入
        i_ret_fun = __fr_mempools_insert(p_g_st_mempool);
        if(i_ret_fun == FR_FAIL)
        {
            pthread_rwlock_unlock(&rwlock_mempool);
            __fr_free_mempool(&p_g_st_mempool);
            return FR_FAIL;
        }

        pthread_rwlock_unlock(&rwlock_mempool);
        return FR_SUCC;
    }

    /*******************************************************************************
    *  FUNC     :  获取当前缓存池的信息
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_slab_stat(char **pp_outbuff)
    {
        int i_size = 4194304;//4M
        int i_used=0, i_data_len, i_idx;
        char *p_buff;
        Frmempool *p_st_mempool = NULL;
        ncx_slab_stat_t st_slab_stat;

        if(g_st_mempool_mng.i_count <= 0)
            return;

        p_buff = __frcomm_malloc(i_size);
        if(!p_buff)
            return;
        *p_buff = '\0';

        pthread_rwlock_rdlock(&rwlock_mempool);
        for(i_idx=0; i_idx < g_st_mempool_mng.i_count; i_idx++)
        {
            memset(&st_slab_stat, 0x00, sizeof(ncx_slab_stat_t));
            p_st_mempool = g_st_mempool_mng.p_mempool[i_idx];
            ncx_slab_stat(p_st_mempool->p_st_sp, &st_slab_stat);
            i_data_len = snprintf(p_buff+i_used, i_size - i_used
                                , "\tname=[%s](%p--%p)\n"
                                  "\t\tpool_size : %zu bytes(%.2fM)\n\t\tused_size : %zu bytes(%.2fM)\n\t\tused_pct  : %zu%%\n"
                                  "\t\ttotal page count : %zu\n\t\tfree page count  : %zu\n"
                                  "\t\tsmall slab use page : %zu,\tbytes : %zu\n\t\texact slab use page : %zu,\tbytes : %zu\n"
                                  "\t\tbig   slab use page : %zu,\tbytes : %zu\n\t\tpage slab use page  : %zu,\tbytes : %zu\n"
                                  "\t\tmax free pages : %zu\n"
                                , p_st_mempool->arr_name
                                , p_st_mempool->p_st_sp->start, p_st_mempool->p_st_sp->end
                                , st_slab_stat.pool_size, st_slab_stat.pool_size/1024.0/1024.0
                                , st_slab_stat.used_size, st_slab_stat.used_size/1024.0/1024.0
                                , st_slab_stat.used_pct
                                , st_slab_stat.pages, st_slab_stat.free_page
                                , st_slab_stat.p_small, st_slab_stat.b_small, st_slab_stat.p_exact, st_slab_stat.b_exact
                                , st_slab_stat.p_big,   st_slab_stat.b_big, st_slab_stat.p_page,  st_slab_stat.b_page
                                , st_slab_stat.max_free_pages
                                );
            i_used += i_data_len;
            if(i_size < i_used + 10)
                break;
        }
        pthread_rwlock_unlock(&rwlock_mempool);

        if(pp_outbuff == NULL)
        {
            printf("memory pool\n%s\n", p_buff);
            __frcomm_free(p_buff);
        }
        else
            *pp_outbuff = p_buff;


        return;
    }

    /*******************************************************************************
    *  FUNC     :  释放当前缓存池
    *  ARGS     :
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_slab_destroy(void)
    {
        int i_idx=0;

        if(p_g_st_mempool == NULL)
            return;

        pthread_rwlock_wrlock(&rwlock_mempool);
        for(i_idx=0; i_idx < g_st_mempool_mng.i_max; i_idx++)
        {
            if(g_st_mempool_mng.p_mempool[i_idx] != p_g_st_mempool)
                continue;

            __fr_mempools_del(i_idx);
            __fr_free_mempool(&p_g_st_mempool);
            pthread_rwlock_unlock(&rwlock_mempool);
            return;
        }

        pthread_rwlock_unlock(&rwlock_mempool);
        return;
    }

    /*******************************************************************************
    *  FUNC     :  内存分配函数(功能同malloc)
    *  ARGS     :  s_size   (内存空间大小)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_slab_malloc(size_t s_size)
    {
        void *p_data=NULL;

        if(s_size <= 0)
            return NULL;
        if(p_g_st_mempool== NULL || p_g_st_mempool->p_st_sp == NULL)
            return NULL;

        // 数据长度 > 100M报警告
        if(s_size > 104857600)
            frwarn("size=%lu (input parameter err?)\n", s_size);
        p_data = ncx_slab_alloc(p_g_st_mempool->p_st_sp, s_size);

        //frdbg("p_data=%p\n", p_data);
        return p_data;
    }

    /*******************************************************************************
    *  FUNC     :  内存数据拷贝，并重新生成内存空间
    *  ARGS     :  p_src    (拷贝数据串地址)
                :  s_size   (拷贝数据大小)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    void *__fr_slab_memdup(const char *p_src, size_t s_size)
    {
        void *p_data;

        if (s_size <= 0)
            return NULL;

        p_data = __fr_slab_malloc(s_size);
        if(!p_data)
            return NULL;

        memcpy(p_data, p_src, s_size);
        return p_data;
    }

    /*******************************************************************************
    *  FUNC     :  字符串拷贝，并重新生成内存空间(功能同strdup)
    *  ARGS     :  p_src    (拷贝字符串地址)
    *  RTN      :  成功返回内存地址，出错返回NULL
    *  NOTE     :
    *******************************************************************************/
    char *__fr_slab_strdup(const char *p_src)
    {
        char *p_data = NULL;
        size_t s_size=0;

        if (p_src == NULL)
            return NULL;

        s_size = strlen(p_src) + 1;
        p_data = __fr_slab_malloc(s_size);
        if(!p_data)
            return NULL;
        memcpy(p_data, p_src, s_size);

        return p_data;
    }

    /*******************************************************************************
    *  FUNC     :  内存释放函数(功能同free)
    *  ARGS     :  p_free_data (释放地址)
    *  RTN      :
    *  NOTE     :
    *******************************************************************************/
    void __fr_slab_free(void *p_free_data)
    {
        int i_ret_fun;
        if (p_free_data == NULL)
            return;

        //frdbg("p_free_data=%p\n", p_free_data);
        i_ret_fun = ncx_slab_free(p_g_st_mempool->p_st_sp, p_free_data);

        if(i_ret_fun == NCX_SLAB_FREE_OUT_POOL)
        {

            Frmempool *p_st_mem_pool;

            pthread_rwlock_rdlock(&rwlock_mempool);
            p_st_mem_pool = __fr_mempools_find(p_free_data);
            if(p_st_mem_pool == NULL || p_st_mem_pool->p_st_sp == NULL)
            {
                pthread_rwlock_unlock(&rwlock_mempool);
                return;
            }

            //frdbg("ncx_slab_free again(%p)\n", p_free_data);
            i_ret_fun = ncx_slab_free(p_st_mem_pool->p_st_sp, p_free_data);
            pthread_rwlock_unlock(&rwlock_mempool);
            if(i_ret_fun != NCX_SLAB_FREE_SUCC)
                frwarn("ncx_slab_free err\n");
        }

        return;
    }
#endif
